package com.wlyuan.open.scanner.repository.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wlyuan.open.constants.CarrierTypeEnum;
import com.wlyuan.open.scanner.application.factory.SyncFactory;
import com.wlyuan.open.scanner.assembler.ScannerTemplateAssembler;
import com.wlyuan.open.scanner.config.GroupProperties;
import com.wlyuan.open.scanner.constant.Constants;
import com.wlyuan.open.scanner.dao.entity.PriceDocIndexDO;
import com.wlyuan.open.scanner.dao.mapper.PriceDocIndexMapper;
import com.wlyuan.open.scanner.domain.quotation.QuotationDetail;
import com.wlyuan.open.scanner.domain.quotation.TemplateDividePriceRange;
import com.wlyuan.open.scanner.domain.template.Template;
import com.wlyuan.open.scanner.domain.template.TemplateConvert;
import com.wlyuan.open.scanner.domain.template.TemplateSpec;
import com.wlyuan.open.scanner.repository.CarrierRepository;
import com.wlyuan.open.scanner.repository.CustomerRepository;
import com.wlyuan.open.scanner.repository.QuotationDetailRepository;
import com.wlyuan.open.scanner.repository.TemplateRepository;
import com.wlyuan.open.scanner.utils.GroupCreateKey;
import com.wlyuan.open.scanner.utils.StreamUtils;
import com.wlyuan.open.scanner.utils.enums.quotation.*;
import com.wlyuan.open.utils.LongUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lijing
 * @description
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TemplateRepositoryImpl implements TemplateRepository {
    private final PriceDocIndexMapper priceDocIndexMapper;
    private final QuotationDetailRepository quotationDetailRepository;
    private final CustomerRepository customerRepository;
    private final CarrierRepository carrierRepository;
    private final SyncFactory syncFactory;
    private final GroupProperties groupProperties;

    @Override
    public void sync() {
        int count = priceDocIndexMapper.count();
        logger.info("合约老数据数据量，size：{}", count);
        var pages = (int) Math.ceil(count * 1.0 / Constants.PAGE_SIZE);
        for (int i = 1; i <= pages; i++) {
            logger.info("开始同步合约：第{}页，共{}页", i, pages);
            var list = priceDocIndexMapper.temlpateIds(i, Constants.PAGE_SIZE);
            list.forEach(id -> {
                try {
                    var data = priceDocIndexMapper.getById(id);
                    var template = parse(data);
                    syncFactory.sync(ScannerTemplateAssembler.assemble(template));
                } catch (Exception e) {
                    logger.error("合约同步异常：{}，{}", id, e);
                }
            });
        }
        logger.info("合约同步完成......");
    }

    @Override
    public List<Template> list() {
        return Collections.emptyList();
    }

    @Override
    public Template getById(Long templateId) {
        return Optional.ofNullable(templateId).map(id -> {
            var quotation = priceDocIndexMapper.getById(id);

            return Optional.ofNullable(quotation).map(data -> parse(data)).orElse(null);
        }).orElse(null);
    }

    @Override
    public List<Long> getByTime(String startTime, String endTime) {
        return priceDocIndexMapper.getByTime(startTime, endTime);
    }

    @Override
    public List<Long> getByTenantIdTime(Long tenantId, String startTime, String endTime) {
        return priceDocIndexMapper.getByTenantIdTime(tenantId, startTime, endTime);
    }

    @Override
    public void getByTenantId(Long tenantId) {
        int count = priceDocIndexMapper.countByTenantId(tenantId);

        logger.info("租户{}合约老数据数据量，size：{}", tenantId, count);
        var pages = (int) Math.ceil(count * 1.0 / Constants.PAGE_SIZE);
        for (int i = 1; i <= pages; i++) {
            logger.info("开始同步租户{}合约：第{}页，共{}页", tenantId, i, pages);
            var list = priceDocIndexMapper.temlpateIdByTenantId(i, Constants.PAGE_SIZE, tenantId);
            list.forEach(id -> {
                try {
                    var data = priceDocIndexMapper.getById(id);
                    var template = parse(data);
                    syncFactory.sync(ScannerTemplateAssembler.assemble(template));
                } catch (Exception e) {
                    logger.error("合约同步异常：{}，{}", id, e);
                }
            });
        }
        logger.info("租户{}合约同步完成.......", tenantId);
    }

    @Override
    public List<Template> getTemplateMatch(Long templateId) {
        LambdaQueryWrapper<PriceDocIndexDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceDocIndexDO::getIndexId, templateId).eq(PriceDocIndexDO::getIndexInvalid, Constants.INVALID_NUMBER_ZERO)
                .eq(PriceDocIndexDO::getIndexType, TemplateTypeEnum.CONTRACT_QUOTE.getValue());
        var list = priceDocIndexMapper.selectList(wrapper);
        return TemplateConvert.convert(list);
    }

    private Template parse(PriceDocIndexDO data) {
        process(data);
        var template = TemplateConvert.convert(data, getSpecs(data.getIndexId()));
        upAndDown(data, template);

        return template;
    }

    private void upAndDown(PriceDocIndexDO data, Template template) {
        //上下游
        if (LongUtils.notNullLong(data.getIndexSupplierid())) {
            data.setCarrierName(data.getPriceSuppliername());
            data.setUpTenantId(data.getIndexSuppliercompanyid());
            //线上承运方
            if (TemplateCreateTypeEnum.PERSONAL.getValue().equals(data.getIndexContractfromtype())) {
                data.setCarrierType(CarrierTypeEnum.ONLINE.getValue());
                data.setUpTenantId(data.getIndexCreatorcompanyid());
                var onlineCarrier = carrierRepository.getOnlineById(data.getIndexSupplierid());
                Optional.ofNullable(onlineCarrier).ifPresent(carrier -> {
                    data.setCarrierName(carrier.getName());
                    data.setUpTenantId(carrier.getTenantId());
                });
            } else {
                //线下承运方
                data.setCarrierType(CarrierTypeEnum.OFFLINE.getValue());
                var offlineCarrier = carrierRepository.getOfflineById(data.getIndexSupplierid());
                Optional.ofNullable(offlineCarrier).ifPresent(carrier -> {
                    data.setCarrierName(carrier.getName());
                    data.setUpTenantId(carrier.getTenantId());
                });
            }
            template.setUpstream(TemplateConvert.upConvert(data));
        }
        if (LongUtils.notNullLong(data.getIndexCustomerid()) && !TemplateCreateTypeEnum.PERSONAL.getValue().equals(data.getIndexContractfromtype())) {
            data.setCustomerName(data.getPriceCustomername());
            data.setCustomerType(data.getIndexContracttype());
            data.setDownTenantId(data.getIndexCreatorcompanyid());
            var customer = Constants.TEMPLATE_ID.contains(data.getIndexId())
                    ? customerRepository.getByCompanyId(data.getIndexCreatorcompanyid(), data.getIndexCustomercompanyid())
                    : customerRepository.getCustomerById(data.getIndexCustomerid());
            Optional.ofNullable(customer).ifPresent(c -> {
                data.setIndexCustomerid(c.getId());
                data.setCustomerType(c.getType());
                data.setCustomerName(c.getName());
                data.setDownTenantId(c.getTenantId());
            });
            template.setDownstream(TemplateConvert.downConvert(data));

        }
    }

    private void process(PriceDocIndexDO data) {
        //强制过期，状态手动变为已关闭
        if (TemplateExpireStatusEnum.YES.getValue().equals(data.getIndexExpiredstatus())) {
            data.setIndexStatus(TemplateStatusEnum.CLOSED.getValue());
        }
        //为过期审核中，状态手动变为过期审核中
        if (TemplateExpireStatusEnum.WAIT.getValue().equals(data.getIndexExpiredstatus())) {
            data.setIndexStatus(TemplateStatusEnum.EXPIRE_WAIT.getValue());
        }
        if (StringUtils.isBlank(data.getIndexName())) {
            data.setIndexName(data.getIndexCode());
        }
        if (groupProperties.groupTenantList().contains(data.getIndexCreatorcompanyid())) {
            data.setIndexCreatorid(0L);
            data.setIndexConfirmer(0L);
        }
    }

    private TemplateSpec getSpecs(Long id) {
        var details = quotationDetailRepository.getDetails(id);
        if (!CollectionUtils.isEmpty(details)) {
            TemplateSpec spec = new TemplateSpec();
            //最低费用
            var min = details.stream().filter(d -> QuotationDetailTypeEnum.MIN.getValue().equals(d.getType())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(min)) {
                spec.setMinimumFee(StreamUtils.sum(min, QuotationDetail::getPrice, BigDecimal::add, BigDecimal.ZERO));
            }
            //保险费率
            var rate = details.stream().filter(d -> QuotationDetailTypeEnum.INSURANCE.getValue().equals(d.getType())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(rate)) {
                spec.setInsuranceRate(StreamUtils.sum(rate, QuotationDetail::getPrice, BigDecimal::add, BigDecimal.ZERO));
            }
            //税率
            var taxRate = details.stream().filter(d -> QuotationDetailTypeEnum.TAX.getValue().equals(d.getType())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(taxRate)) {
                spec.setTaxRate(Optional.ofNullable(StreamUtils.max(taxRate, Comparator.comparing(QuotationDetail::getId))).map(QuotationDetail::getPrice).orElse(BigDecimal.ZERO));
            }

            //明细 过滤 最低费用，保险，税率类型
            details = details.stream().filter(d -> QuotationDetailTypeEnum.TEMPLATE_SPEC_TYPE.contains(d.getType())).collect(Collectors.toList());
            //明细根据类型分组
            var map = StreamUtils.groupBy(details, QuotationDetail::getType);
            //零担
            var divide = map.get(QuotationDetailTypeEnum.LESS_LOAD.getValue());
            if (!CollectionUtils.isEmpty(divide)) {
                //零担
                divide(divide, spec);
            }
            //整车费
            spec.setWhole(map.get(QuotationDetailTypeEnum.FULL_LOAD.getValue()));
            //提货费
            var pick = map.get(QuotationDetailTypeEnum.PICK.getValue());
            if (!CollectionUtils.isEmpty(pick)) {
                spec.setPick(groupDivide(pick));
            }
            //送货费
            var deliver = map.get(QuotationDetailTypeEnum.DELIVERY.getValue());
            if (!CollectionUtils.isEmpty(deliver)) {
                spec.setDeliver(groupDivide(deliver));
            }
            //装货费
            var load = map.get(QuotationDetailTypeEnum.ON_LOAD.getValue());
            if (!CollectionUtils.isEmpty(load)) {
                spec.setLoad(groupDivide(load));
            }
            //卸货费
            var offLoad = map.get(QuotationDetailTypeEnum.OFF_LOAD.getValue());
            if (!CollectionUtils.isEmpty(offLoad)) {
                spec.setUnload(groupDivide(offLoad));
            }
            return spec;
        }
        return null;
    }

    private void divide(List<QuotationDetail> list, TemplateSpec spec) {
        spec.setDivide(groupDivide(list));
    }

    /**
     * 零担-处理价格区间
     *
     * @param list
     * @return
     */
    private List<QuotationDetail> groupDivide(List<QuotationDetail> list) {
        //根据地区分组
        var addressMap = list.stream().collect(Collectors.groupingBy(TemplateRepositoryImpl::groupByAddressId));
        return addressMap.keySet().stream().map(key -> {
            var detailData = addressMap.get(key);
            //基础数据相同（省市区，类型，时效，价格单位）
            var base = detailData.stream().findFirst().get();
            //区间数据，根据最大值，最小值，价格去重
            var pricingList = StreamUtils.distinct(range(detailData),
                    Comparator.comparing(o -> GroupCreateKey.createKey(String.valueOf(o.getMinValue()), String.valueOf(o.getMaxValue()), String.valueOf(o.getPrice()))),
                    false);
            //最大值和最小值相同，根据id取最小的
            pricingList = StreamUtils.minByGroup(pricingList, item -> item.getMinValue().add(item.getMaxValue()), Comparator.comparing(TemplateDividePriceRange::getId)).values().stream().collect(Collectors.toList());
            //根据最小值升序
            base.setPricingList(pricingList.stream().sorted(Comparator.comparing(TemplateDividePriceRange::getMinValue)).collect(Collectors.toList()));
            return base;
        }).collect(Collectors.toList());
    }

    private static String groupByAddressId(QuotationDetail detail) {
        String from = StringUtils.isBlank(detail.getFromAddress()) ? "null" : detail.getFromAddress();
        String to = StringUtils.isBlank(detail.getToAddress()) ? "null" : detail.getToAddress();
        return GroupCreateKey.createKey(String.valueOf(detail.getFromProvince()), String.valueOf(detail.getFromCity()), String.valueOf(detail.getFromDistrict()), from,
                String.valueOf(detail.getToProvince()), String.valueOf(detail.getToCity()), String.valueOf(detail.getToDistrict()), to, String.valueOf(detail.getBillingUnit()));
    }

    /**
     * 整车区间数据
     *
     * @param list
     * @return
     */
    private List<TemplateDividePriceRange> range(List<QuotationDetail> list) {
        return list.stream().map(range -> new TemplateDividePriceRange(range.getId(), range.getMinValue(), range.getMaxValue(), range.getPrice())).collect(Collectors.toList());
    }

    private List<TemplateDividePriceRange> range(QuotationDetail detail) {
        if (!Optional.ofNullable(detail).isPresent()) {
            return Collections.emptyList();
        }
        return new ArrayList<TemplateDividePriceRange>() {


            private static final long serialVersionUID = 6824742445654724186L;

            {
                add(new TemplateDividePriceRange(null, detail.getMinValue(), detail.getMaxValue(), detail.getPrice()));
            }
        };
    }

}
